/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry.spring;

import io.sentry.EventProcessor;
import io.sentry.IpAddressUtils;
import io.sentry.SentryBaseEvent;
import io.sentry.SentryEvent;
import io.sentry.SentryOptions;
import io.sentry.protocol.SentryTransaction;
import io.sentry.protocol.User;
import io.sentry.util.Objects;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * @deprecated instead of using event processor for setting user on events and transactions, user
 * should be set on the scope using {@link SentryUserFilter}.
 */
@Deprecated
public final class SentryUserProviderEventProcessor implements EventProcessor {
    private final @NotNull SentryOptions options;
    private final @NotNull SentryUserProvider sentryUserProvider;

    public SentryUserProviderEventProcessor(
        final @NotNull SentryOptions options, final @NotNull SentryUserProvider sentryUserProvider) {
        this.options = Objects.requireNonNull(options, "options is required");
        this.sentryUserProvider =
            Objects.requireNonNull(sentryUserProvider, "sentryUserProvider is required");
    }

    @Override
    public @NotNull SentryEvent process(
        final @NotNull SentryEvent event, final @Nullable Object hint) {
        mergeUser(event);

        return event;
    }

    @NotNull
    @ApiStatus.Internal
    public SentryUserProvider getSentryUserProvider() {
        return sentryUserProvider;
    }

    private void mergeUser(final @NotNull SentryBaseEvent event) {
        final User user = sentryUserProvider.provideUser();
        if (user != null) {
            final User existingUser = Optional.ofNullable(event.getUser()).orElseGet(User::new);

            Optional.ofNullable(user.getEmail()).ifPresent(existingUser::setEmail);
            Optional.ofNullable(user.getId()).ifPresent(existingUser::setId);
            Optional.ofNullable(user.getIpAddress()).ifPresent(existingUser::setIpAddress);
            Optional.ofNullable(user.getUsername()).ifPresent(existingUser::setUsername);
            if (user.getOthers() != null && !user.getOthers().isEmpty()) {
                Map<String, String> existingUserOthers = existingUser.getOthers();
                if (existingUserOthers == null) {
                    existingUserOthers = new ConcurrentHashMap<>();
                }
                for (Map.Entry<String, String> entry : user.getOthers().entrySet()) {
                    existingUserOthers.put(entry.getKey(), entry.getValue());
                }
                existingUser.setOthers(existingUserOthers);
            }
            event.setUser(existingUser);
        }
        if (options.isSendDefaultPii()) {
            final User existingUser = event.getUser();
            if (existingUser != null && IpAddressUtils.isDefault(existingUser.getIpAddress())) {
                // unset {{auto}} as it would set the server's ip address as a user ip address
                existingUser.setIpAddress(null);
            }
        }
    }

    @Override
    public @NotNull SentryTransaction process(
        final @NotNull SentryTransaction transaction, final @Nullable Object hint) {
        mergeUser(transaction);

        return transaction;
    }
}
